import { Mesh, LinearFilter, Texture, PlaneGeometry, AlwaysStencilFunc, KeepStencilOp, NotEqualStencilFunc, ReplaceStencilOp, Vector3 } from "three";
import GeoUtils from "../../utils/GeoUtils";
import Helper from "../../utils/Helper";
import DEMBufferGeometry from "../../renderExtension/geometriy/DEMBufferGeometry";
import { App } from "../../Engine";
import * as Lerc from 'lerc';
import TilePort from "../../component/models/TilePort";
import MeshUtil from "../../renderExtension/utils/MeshUtil";
import Events from "../../application/constants/Events";
import ParseBundleTile from "../utils/ParseBundleTile";
import Const from "../../application/constants/Const";
import { CustomMeshBasicMaterial } from "../../renderExtension/Material/CustomMaterial";
import ShaderConst from "../../renderExtension/Material/ShaderConst";

/**
 * 基础瓦片
 */
export default class DemTile extends Mesh implements TilePort{
    protected url: string;
    protected demUrl: string;
    protected demScale: number;
    private demValue;
    private demHeightAdjust: number;
    private demWidth: any;
    private demHeight: any;
    protected options: any;
    protected callbackLoadSuccess;
    public material: CustomMeshBasicMaterial;
    private static defaultTexture: Texture; //默认的纹理
    private static tileGeometry: PlaneGeometry;
    private bloomData: {isOpenBloom: boolean, bloomPointArray: Array<number>, bloomPointCount: number, bloomHeight: number};
    private bloomPositionArray;
    private bloomInitHeight;
    private bloomPointCount: number;
    private domImageHttpRequest;
    private demXmlHttpRequest: XMLHttpRequest;
    private lrc;

    /**
     * 基础瓦片的构造函数
     * @param options 构造函数的参数
     * options.url 影像底图的url
     * options.urlDecimal 影像底图url中的瓦片行列号是否是十进制，true表示十进制，false表示十六进制
     * options.demUrl dem底图的url
     * options.demUrlDecimal dem底图url中的瓦片行列号是否是十进制，true表示十进制，false表示十六进制
     * options.demScale dem高度的夸张值
     * options.demHeightAdjust 一个高度值，用于调整dem底图的高度
     * options.useDefaultTexture 是否使用默认瓦片底图，当瓦片的影像纹理请求还没有成功的时候，瓦片先使用默认瓦片底图
     * options.loaded 纹理图片加载成功后的回调函数
     * options.curzoom 当前地图的层级
     * options.lrc 瓦片行列号的json数据
     * options.zIndex 为了避免深度冲突，调整高度值
     * options.isAdditionalLayer 是否是附加图层。如果是附件图层，没有数据的瓦片不显示。这样做是为了避免附件图层中没有数据的瓦片会遮挡下面的图层
     */
    constructor(options: any) {
        super(undefined, undefined);
        this.options = options;
        this.demScale = options.demScale;
        this.callbackLoadSuccess = options.loaded;

        if(!this.setTile(options)){ //设置瓦片的位置和图片的url
            return;
        } 
        this.setGeometry(options);
        this.setMaterial(options);

        Helper.enablePolygonOffset(this.material, App.getInstance().options.Map.maxZoom - this.lrc.z, options.zIndex);
    }

    private setTile(options){
        this.lrc = options.lrc;
        let lrc = this.lrc;
        let cx = GeoUtils.getCDevX(lrc.x, lrc.z, 0.5);
        let cy = GeoUtils.getRDevY(lrc.y, lrc.z, 0.5);

        this.position.set(cx, cy, 0);
        this.url = Helper.getTileUrl(this.options.url, lrc.x, lrc.y, lrc.z, (this.options.urlDecimal === undefined) ? true : this.options.urlDecimal);//options.url;
        this.demUrl = Helper.getTileUrl(this.options.demUrl, lrc.x, lrc.y, lrc.z, (this.options.demUrlDecimal === undefined) ? true : this.options.demUrlDecimal);
        this.demHeightAdjust = options.demHeightAdjust;
        this.bloomPointCount = 0;
        return true;
    }

    private setGeometry(options){
        let tileWidth = GeoUtils.getTileWidth(options.curzoom);
        let self = this;
        this.requestDem(() => {
            self.geometry = new DEMBufferGeometry(self.demValue, self.demHeightAdjust, self.demWidth, self.demHeight, tileWidth, tileWidth, 10, 10);
            
            App.getInstance().fireEvent(Events.AdjustModelHeight, self.parent); //通知其他模型根据这个dem瓦片的数据调整高度
        });

        if (!DemTile.tileGeometry || DemTile.tileGeometry['tileWidth'] != tileWidth) { //一个层级只使用一个几何体
            DemTile.tileGeometry = new PlaneGeometry(tileWidth, tileWidth, 1, 1);
            DemTile.tileGeometry['tileWidth'] = tileWidth;
        }
        this.geometry = DemTile.tileGeometry;
    }

    
    public updateVisibility(){
        this.material.stencilRef = Const.StencilRefValue.DemTileRef;
        this.material.stencilFunc = NotEqualStencilFunc;
        this.material.stencilFail = KeepStencilOp;
        this.material.stencilZFail = KeepStencilOp;
        this.material.stencilZPass = KeepStencilOp;
        this.material.needsUpdate = true;
    }

    public restoreVisibility(){
        this.material.stencilRef = Const.StencilRefValue.DemTileRef;
        this.material.stencilFunc = AlwaysStencilFunc;
        this.material.stencilFail = ReplaceStencilOp;
        this.material.stencilZFail = ReplaceStencilOp;
        this.material.stencilZPass = ReplaceStencilOp;
        this.material.needsUpdate = true;
    }

    private setMaterial(options){
        let macrosList:Array<string> = new Array<string>();
        macrosList.push(ShaderConst.macrosConfig.contourLine); //用于等高线的shader宏命令
        
        let materialParam = {
            coreParameters: {
                depthTest: true,

                //设置模版缓冲区，用2覆盖模版缓冲区
                stencilWrite: true,
				stencilRef: Const.StencilRefValue.DemTileRef,
                stencilFuncMask: 0xff,
				stencilFunc: AlwaysStencilFunc,
				stencilFail: ReplaceStencilOp,
				stencilZFail: ReplaceStencilOp,
				stencilZPass: ReplaceStencilOp,
            },
            macrosList: macrosList,
            identity: 'demTileMaterial'
        };
        this.material = new CustomMeshBasicMaterial(materialParam);

        if (!DemTile.defaultTexture) {
            let defaultImage = ''
            DemTile.defaultTexture = Helper.createTexture(defaultImage);
        }
        if (options.useDefaultTexture) {
            this.material.map = DemTile.defaultTexture;
            this.material.color.set(1, 1, 1);
        }else{
            this.material.color.set(App.getInstance().options.Map.tileDefaultColor);
        }

        if(options.isAdditionalLayer){ //可选图层，只有当数据加载成功的时候才显示。这样做是为了避免默认遮挡住下边的图层
            // this.visible = false;
            this.material.transparent = true;
        }

        this.requestDom(this.url);
    }

    public openBloom(positionArray: Array<number>, initHeight){

        this.bloomData = {isOpenBloom: true, bloomPointArray: positionArray, bloomPointCount: positionArray.length, bloomHeight: initHeight};
        this.material['setBloom'](this.bloomData);
    }

    public updateBloomHeight(currentHeight){
        if(!this.bloomData){
            return;
        }

        this.bloomData.bloomHeight = currentHeight;
        this.material['setBloom'](this.bloomData);
    }

    public closeBloom(){

        this.bloomData = {isOpenBloom: false, bloomPointArray: new Array<number>(), bloomPointCount: 0, bloomHeight: 0};
        this.material['setBloom'](this.bloomData);
        this.bloomData = null;
    }

    private requestDom(url) {
        if (!url) return;

        let parseBundle:ParseBundleTile = this.options['parseBundle'];
        if (!parseBundle) { //松散型瓦片
            let imageTexture = Helper.createTexture(url, this.callbackTexture.bind(this), true);
            this.domImageHttpRequest = imageTexture['imageLoader'];
        }else{ //紧凑型瓦片
            let self = this;
            parseBundle.getTile(this.options.lrc.x, this.options.lrc.y, this.options.lrc.z, function (imageData) {
                let imageTexture = Helper.createTextureWithData(imageData, self.callbackTexture.bind(self));
                self.domImageHttpRequest = imageTexture['imageLoader'];
            });
        }
    }

    protected requestDem(callback) {
        if (!this.demUrl) return;
        this.reqestDemInner(callback);
    }

    private callbackTexture(isSuccess, texture){
        if (isSuccess) {
            this.visible = true;
            texture.magFilter = LinearFilter;
            texture.minFilter = LinearFilter;
            this.material.map = texture;
            this.material.transparent = false;
            this.material.needsUpdate = true;
            this.material.color.set(1, 1, 1);
            // scope.material.depthTest = false;
            if (this.callbackLoadSuccess instanceof Function) {
                this.callbackLoadSuccess(this);
            }

            this.domImageHttpRequest = null;
        }else{
            if(this.options.isAdditionalLayer){
                MeshUtil.dispose(this);
            }
        }

        if(!this.options.isAdditionalLayer){ //非附加图层，每次创建瓦片都要向外发送广播通知
            App.getInstance().fireEvent(Events.AddTile); 
        }
    }

    /**
        * 对请求的DEM数据进行解析
    */
    private reqestDemInner(callback) {
        var me = this;
        this.demXmlHttpRequest = new XMLHttpRequest();
        this.demXmlHttpRequest.responseType = "arraybuffer";
        this.demXmlHttpRequest.open('GET', this.demUrl);
        this.demXmlHttpRequest.onload = function (response: any) {
            if (response.target.status === 404) {
                callback();
                return;
            }
            var lerc = Lerc.decode(response.target.response, { noDataValue: 0 });
            me.demValue = lerc.pixels[0];
            me.demWidth = lerc.width;
            me.demHeight = lerc.height;
            let stats = lerc.statistics[0];
            callback();

            me.demXmlHttpRequest = null;
        };
        this.demXmlHttpRequest.onerror = function () {
            callback();

            me.demXmlHttpRequest = null;
        };
        this.demXmlHttpRequest.send();
    }

    public cancleNetTask(){
        if (this.domImageHttpRequest) {
            if(this.domImageHttpRequest instanceof XMLHttpRequest){
                this.domImageHttpRequest.abort();
                this.domImageHttpRequest.onload = null;
                this.domImageHttpRequest.onerror = null;
            }else{
                this.domImageHttpRequest.src = '';
            }
        }
        if(this.demXmlHttpRequest){
            this.demXmlHttpRequest.abort();
        }
    }

    public dispose() {
        this.cancleNetTask();
        MeshUtil.dispose(this);
    }

    setCut(clapData: { isOpenClap: boolean; clapPointArray: []; clapPointCount: number; renderSide: number; }) {
        /**
         * 压平分析在dem数据中不能用，目前存在的问题如下：
           1.polygonMesh上面一片黑色，可能是在预渲染的时候没有拿到数据（只是猜测而已）
           2.polygonMesh没有开启深度检测，造成深度紊乱
           3.没有考虑压平的侧面问题，造成倾斜相机的时候侧面一片空白。可以估算出压平区域dem的最大高度值，生成墙壁mesh几何体的时候使用这个高度值，墙壁mesh几何体生成完毕后，
             可以在渲染阶段利用模版缓冲测试和深度缓冲测试使得在dem以下的墙壁可见，dem以上的墙壁不可见。并且墙壁的纹理可以在预渲染纹理的边缘处采样
         */
        this.material['setCut'](clapData);
    }
}